#Imports go here:
import pygame.font, pygame.event, pygame.draw, string
import pygame,sys,socket,time, traceback
import sys
from pygame.locals import *
from deck import *
from threading import Thread
from Queue import Queue
from card import Card
from player import addAchievement, checkAchievement, achievementList, Player

#Images to be loaded go here:
lScreen = pygame.image.load("images/drazah_lobby.png")
dScreen = pygame.image.load("images/drazah_draw.png")
wScreen = pygame.image.load("images/awaiting.png")
gui = pygame.image.load("images/drazah_bg.png")
goLogo = pygame.image.load("images/player_go.png")
goLogo = pygame.image.load("images/player_go.png")
rScreen = pygame.image.load("images/drazah_review.png")
dealingLogo = pygame.image.load("images/dealing.png")
eOGScreen = pygame.image.load("images/game_over.png")
playAgainButton = pygame.image.load("images/play_again.png")
quitButton = pygame.image.load("images/quit.png")

#achievement list
icons = []
for i in range (0,9):
    icons.append(pygame.image.load("images/A_%d.png"%i))

#waiting animation
awaiting_img = []
animcount = 0
for i in range (1,16):
    awaiting_img.append(pygame.image.load("images/waiting/%d.gif"%i))

achievements=0
newAchievementDisplay=[] #For storing new achievements ready to display in debrief
#Class for connection thread


def drawBigAchievements(screen,nums):
    size=100
    for i in range(len(nums)):
        xpos=35+i*size
        ypos=435
        screen.blit(pygame.transform.smoothscale(icons[nums[i]],(size,size)).convert_alpha(),(xpos,ypos))

class ConnectionMonitor(Thread):

    def __init__(self, ip, port, incoming, outgoing):
        self.ip=ip
        self.port=port
        self.incoming=incoming
        self.outgoing=outgoing
        Thread.__init__(self)

    def run(self):

        sys.stdout.write("Running")
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((self.ip, self.port))
        s.setblocking(0)
        stack=""
        while 1:
            #Any data?
            try:
                data=s.recv(4096)
                stack+=data
            except:
                pass #no data
            if not self.outgoing.empty():
                data=self.outgoing.get()
                s.sendall(data)
            while stack.find("\n")!=-1:
                pos=stack.find("\n")
                data=stack[:pos]
                stack=stack[pos+1:]
                self.incoming.put(data.strip())
            time.sleep(1)
            
#Functions go here:
def get_key():
  while 1:    
    pass

#Display text-entry box for player to enter name.
def display_box(screen, message):
  "Print a message in a box in the middle of the screen"
  screen.blit(lScreen,(0,0))
  if len(message) != 0:
    screen.blit(fontobject.render(message, 1, (0,0,0)),(50,150))
    pygame.display.flip()                 
#Display text box and run event loop for keyboard and mouse.
def ask(screen, question):
    "ask(screen, question) -> answer"
    pygame.font.init()
    current_string = []
    while 1:
        display_box(screen, question + ": " + string.join(current_string,""))
        inkey=None
        mousePos=None
        mousePressed=False
        event = pygame.event.poll()
        if event.type == KEYDOWN:
            inkey=event.key
        elif event.type == MOUSEBUTTONDOWN:
            mousePos=event.pos
            mousePressed=True
      
        if inkey!=None:
            if inkey == K_BACKSPACE:
                current_string = current_string[0:-1]
                
            elif inkey == K_RETURN:
                break
            elif inkey == K_MINUS:
                current_string.append("_")
            elif inkey <= 127:
                current_string.append(chr(inkey))
                display_box(screen, question + ": " + string.join(current_string,""))
        elif mousePos!=None:
            if mousePressed and startRect.collidepoint(mousePos):
                break
    return string.join(current_string,"")




#Quit function
def quitGame():
    conn.outgoing.put("END:QUIT")
    sys.exit(0)

#Lobby screen
w,h=size=(1024,768)
logo_prop=17.37

#Rectangles for each deck
hRect=pygame.Rect(10,508,400,250)
aRect=pygame.Rect(414,508,400,250)
stackRect=pygame.Rect(312,50,400,100)

#Rectangles for play again and quit buttons
pAgainRect=pygame.Rect(54,573,382,127)
quitRect=pygame.Rect(600,573,382,127)

#Rectangle for start game button
startRect=pygame.Rect(631,486,332,137)

#Set colour values
black=(0,0,0)
white=(255,255,255)
bg=(0,0,40)
fg=white

#Boolean for request highlighting (selection)
myTurn = False

#Initiate pygame and set screen size and font
pygame.init()
screen = pygame.display.set_mode(size)
pygame.display.set_caption('drazaH')
fontobject = pygame.font.Font(None,58)
pName=""
#Set loop to run `ask' function until we have a player name
while pName=="": pName =  ask(screen, "Name")

#Set play to True and start running game
play=True
pygame.font.init()
sysfont=pygame.font.Font(None,12)
clock=pygame.time.Clock()

#Connect to Drazah server (after listening for address)
host = ""
port = 52133
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s.bind((host,port))

#Run loop to listen for drazaH server address
while 1:
    try:
        message,address = s.recvfrom(8192)
        if message.startswith("DRAZAH:"):
            gameAddress,port = message[7:].strip().split(":")
            port=int(port)
            if len(gameAddress) > 0:
                break
        else:
            pass
    except (KeyboardInterrupt, SystemExit):
        raise
    except:
        traceback.print_exc()

#Start a connection to the game server
conn=ConnectionMonitor(gameAddress,port,Queue(),Queue())
conn.start()
log=[]
hazardDeck = None
actionDeck = None
stack = None
state = "AWAITING"
message=""
score = 0
time.sleep(2)

#True when in "round debrief mode"
inRDebrief=False
dbrfInfo=None
req=None
countdown=0.0
oName="no player"
rndScores = ""
dbrfScores = []
winner = None
#To turn off/on ingame display
ingameDisplay = True
while play:
    if state!="AWAITING": message=""
    ingameDisplay=True
    hoverCard=None
    screen.fill(bg)
    y=0
    while not conn.incoming.empty() and not inRDebrief:
        item=conn.incoming.get()

        #get players hazard deck from log
        if item.startswith("DECKH:"):
            hazardDeck=eval(item[6:])
            for c in hazardDeck.cards:
                c.loadImage()
                
        #Give player name to server
        elif item.startswith("REQ_NAME"):
            conn.outgoing.put("NAME:%s"%pName)
            
        #Get players action deck from log
        elif item.startswith("DECKA:"):
            actionDeck=eval(item[6:])
            for c in actionDeck.cards:
                c.loadImage()

        #Read log for current game stack, load images of cards in stack
        elif item.startswith("STACK:"):
            stack=eval(item[6:])
            for stackItem in stack:
                stackItem.loadImage()

        #Check the log for a state up date
        elif item.startswith("STATE:"):
            newState=item[6:].strip()
            state = newState

        #Read log for player score, update with new value
        elif item.startswith("SCORE:"):
            newScore = item[6:].strip()
            score = newScore
            req=None

        #Read log for a server request
        elif item.startswith("REQ:"):
            req = item[4:].strip()
            myTurn = True

        #Read Opponent info sent from game
        elif item.startswith("OPPONENT:"):
            oName = item[9:].strip()
        elif item.startswith("PLAYER:"):
            pName = item[7:].strip()

        #Read info and set round debrief phase if necessary 
        elif item.startswith("RDEBRIEF:"):
            inRDebrief=True
            dbrfInfo=eval(item[9:])
            countdown= 1000.0
            req=None
            for i in  dbrfInfo["scores"]:
                dbrfScores.append(i)

        #Set up base achievements so stuff pulled from DB don't get
        #flagged as "new"
        elif item.startswith("BASE_ACHIEVEMENTS:"):
            achievements=int(item[18:].strip())
        #Read info for new achievements gained
        elif item.startswith("ACHIEVEMENTS:"):
            newAchievments=int(item[13:].strip())
            if newAchievments!=achievements:
                added=achievements^newAchievments
                for i in achievementList(added):
                    print "Player just earned achievement %d"%i
                    newAchievementDisplay.append(i)
            achievements=newAchievments

            
            
        #Read log to get both player scores and determine who is the winner
        elif item.startswith("GAMEDETAILS:"):
            pDetails = eval(item[12:])
            print "\n\n","-"*20,"\n",item[12:],"\n","-"*20,"\n\n"
        
            for i in pDetails["pDetails"]:
                for n in range (len(i)):
                    #Check both player scores to see who won
                    if i[0].score > i[1].score:
                        print "\n\nFIRST PLAYER WINS\n\n"
                        winner = i[0]
                        loser = i[1]
                    elif i[1].score > i[0].score:
                        print "\n\nSECOND PLAYER WINS\n\n"
                        winner = i[1]
                        loser = i[0]
                    elif i[0].score == i[1].score:
                        print "\n\nNOBODY WINS>\n\n"
                        winner = None
                        loser = None
                        screen.blit(dScreen,(0,0))

        #Eval data for side display
        elif item.startswith("WINS:"):
            displayWins = eval(item[5:])

        elif item.startswith("LOSSES:"):
            displayLosses = eval(item[7:])
                
        elif item.startswith("DRAWS:"):
            displayDraws = eval(item[6:])

        #Eval opponent data for side display
        elif item.startswith("OWINS:"):
            displayOWins = eval(item[6:])

        elif item.startswith("OLOSSES:"):
            displayOLosses = eval(item[8:])
                
        elif item.startswith("ODRAWS:"):
            displayODraws = eval(item[7:])

        elif item.startswith("OACHS:"):
            displayOAchs = eval(item[6:])

        #Reset command for it opponent drops out
        elif item.startswith("RESET:"):
            message = item[6:].strip()
            print "Opponent dropped: %s"%(message)
            hazardDeck = None
            actionDeck = None
            stack = None
            state = "AWAITING"
            #state = ""
            score = 0
            inRDebrief=False
            dbrfInfo=None
            req=None
            countdown=0.0
            pName="no player"
            oName="no player"
            rndScores = ""
            dbrfScores = []
            winner = None
            ingameDisplay = True
            newAchievementDisplay=[]
            break

        log.append(item)

    #Display `Waiting' screen if state equal awaiting (turn off ingameDisplay, player logo and dealing logo)
    if state == "AWAITING":
        if animcount < 15:
            screen.blit(awaiting_img[animcount],(0,0))
            animcount += 1
            #TODO: This is not ideal!
            time.sleep(0.3)
        else:
            animcount = 0
        
        if message!="":
            screen.blit(fontobject.render(message,1,(0,0,0)),(280,110))
        pygame.display.flip()
        ingameDisplay = False
    # else:              
    #     screen.blit(gui,(0,0))        

    #Draw player turn logos if ingameDisplay equals true
    if ingameDisplay == True:
        screen.blit(gui,(0,0))
        #Player 0 stats (left side)
        #Display name
        font = pygame.font.Font(None, 18)
        text = font.render(pName, 1, (10, 10, 10))
        textpos = text.get_rect()
        textpos =(61,145)
        screen.blit(text, textpos)
        #Display Stats
        tWins = font.render("%s"%displayWins,1,(10,10,10))
        tLosses = font.render("%s"%displayLosses,1,(10,10,10))
        tDraws = font.render("%s"%displayDraws,1,(10,10,10))
        tWinspos = text.get_rect()
        tWinspos = (98,170)
        tLossespos = text.get_rect()
        tLossespos = (98,194)
        tDrawspos = text.get_rect()
        tDrawspos = (98,218)
        screen.blit(tWins,tWinspos)
        screen.blit(tLosses,tLossespos)
        screen.blit(tDraws,tDrawspos)
        #Display Player 0 achievements icons (left side)
        startX = 30
        startY = 250
        size = 60
        sizeY = 43
        #Draw players achievements
        for a in achievementList(achievements):
            xp = startX + (a%2)*size
            yp = startY + (a/2)*sizeY
            image = icons[a]
            image = pygame.transform.smoothscale(image,(25,25)).convert_alpha()
            screen.blit(image,(xp,yp))


        #Player 1 stats (right side)
        #Display name
        font = pygame.font.Font(None, 18)
        text2 = font.render(oName, 1, (10, 10, 10))
        text2pos = text.get_rect()
        text2pos =(894,145)
        screen.blit(text2, text2pos)
        #Display Stats
        tOWins = font.render("%s"%displayOWins,1,(10,10,10))
        tOLosses = font.render("%s"%displayOLosses,1,(10,10,10))
        tODraws = font.render("%s"%displayODraws,1,(10,10,10))
        tOWinspos = text.get_rect()
        tOWinspos = (898,170)
        tOLossespos = text.get_rect()
        tOLossespos = (898,194)
        tODrawspos = text.get_rect()
        tODrawspos = (898,218)
        screen.blit(tOWins,tOWinspos)
        screen.blit(tOLosses,tOLossespos)
        screen.blit(tODraws,tODrawspos)
        #Display Player 1 achievements icons (right side)
        startX = 910
        startY = 250
        size = 60
        sizeY = 43
        #Draw players achievements
        for a in achievementList(displayOAchs):
            xp = startX + (a%2)*size
            yp = startY + (a/2)*sizeY
            image = icons[a]
            image = pygame.transform.smoothscale(image,(25,25)).convert_alpha()
            screen.blit(image,(xp,yp))

        #Draw logo for players turn
        if myTurn == True:
            screen.blit(goLogo,(230,55))
        elif myTurn == False:
            screen.blit(goLogo,(230,92))
            
    #Dealing logo
    if ingameDisplay == True:
        dealingLogo = pygame.transform.smoothscale(dealingLogo,(175,175)).convert_alpha()
        screen.blit(dealingLogo,(425,50))
          
   #Draw players hazard cards from deck
    if hazardDeck!=None:
        #Draw hazard cards in slot
        ncards=len(hazardDeck)
        bw=hRect.width
        highlight=None
        for i in range(ncards):
            xpos=hRect.left+i*bw/ncards
            c=hazardDeck.cards[i]
            c.rect.left=xpos
            c.rect.top=515
            if req=="H" and \
                        not inRDebrief and \
                        highlight==None and \
                        c.rect.collidepoint(pygame.mouse.get_pos()) and \
                        myTurn == True:
                highlight=i
                hoverCard=c
            else:
                c.draw(screen)
        if highlight!=None:
            c=hazardDeck.cards[highlight]
            c.rect.move_ip(0,-50)
            c.draw(screen)
            
    #Draw players action cards from deck
    if actionDeck !=None:
        #Draw action cards in slot
        ncards=len(actionDeck)
        bw=aRect.width
        highlight = None
        for i in range(ncards):
            xpos=aRect.right-i*bw/ncards
            c=actionDeck.cards[i]
            c.rect.left=xpos
            c.rect.top=515
            if req=="A" and \
                   not inRDebrief and \
                   highlight == None and \
                   c.rect.collidepoint(pygame.mouse.get_pos()) and \
                   myTurn == True:
                highlight=i
                hoverCard=c
            else:
                c.draw(screen)
        if highlight!=None:
            c=actionDeck.cards[highlight]
            c.rect.move_ip(0,-50)
            c.draw(screen)

    #Draw round review screen
    if inRDebrief:
        screen.blit(rScreen,(0,0))
        drawBigAchievements(screen,newAchievementDisplay)                
    #Draw stack
    if stack !=None:
        ncards=len(stack)
        bw=stackRect.width
        #If the game is NOT in debrief mode, draw the stack in standard position
        if not inRDebrief:
            for i in range(ncards):
            #Draw the situation card above the others due to different shape
                if i == 0:
                    xpos=stackRect.left+i*bw/ncards
                    c=stack[i]
                    c.rect.left=xpos+75
                    c.rect.top=50
                    c.draw(screen)
            #Other cards in stack that need to be drawn
                else:
                    if i == 1:
                        xpos = stackRect.left+i*bw/ncards
                        c=stack[i]
                        c.rect.left= xpos-200
                        c.rect.top = 200
                        c.draw(screen)
                    else:
                        xpos = stackRect.left+i*bw/ncards
                        c=stack[i]
                        c.rect.left= xpos
                        c.rect.top = 200
                        c.draw(screen)
        #If the game is in debrief mode, draw the stack in review position
        else:
            for i in range(ncards):
                #Situation card in stack (review position)
                if i == 0:
                    xpos= 50
                    c=stack[i]
                    c.rect.left=xpos
                    c.rect.top=200+25
                    c.draw(screen)
                else:
                    #hazard card in stack (review position)
                    if i == 1:
                        c=stack[i]
                        c.rect.left= xpos + 350
                        c.rect.top = 200
                        c.draw(screen)
                    #Action card in stack (review position)
                    else:
                        c=stack[i]
                        c.rect.left= xpos + 700
                        c.rect.top = 200
                        c.draw(screen)
            

                
              
    #Draw the ingameDisplay details (players scores and ID's)
    if ingameDisplay == True:
            #Display score
            font = pygame.font.Font(None, 45)
            text = font.render("%s"%score, 1, (10, 10, 10))
            textpos = text.get_rect()
            textpos =(945,30) 
            screen.blit(text, textpos)
            #Display player ID
            font = pygame.font.Font(None, 36)
            text = font.render("%s"%pName, 1, (10, 10, 10))
            textpos = text.get_rect()
            textpos =(20,60) 
            screen.blit(text, textpos)
            #Display opponent ID
            font = pygame.font.Font(None, 36)
            text = font.render("%s"%oName, 1, (10, 10, 10))
            textpos = text.get_rect()
            textpos =(20,95) 
            screen.blit(text, textpos)
            
    #Draw end of game details (winner name & score and loser name & score)
    if state == "END":
        #End of game page
        playAgainRect=playAgainButton.get_rect().move(54,573)
        quitRect=quitButton.get_rect().move(600,573)
        while 1:
            if winner!=None:
                screen.blit(eOGScreen,(0,0))     
                #Display Winner name
                font = pygame.font.Font(None, 70)
                text = font.render("%s"%winner.name, 1, (10, 10, 10))
                textpos = text.get_rect()
                textpos =(60,190) 
                screen.blit(text, textpos)
                #Display Winner score
                font = pygame.font.Font(None, 55)
                text = font.render("%s"%winner.score, 1, (10, 10, 10))
                textpos = text.get_rect()
                textpos =(309,347) 
                screen.blit(text, textpos)
                #Display Loser name
                font = pygame.font.Font(None, 70)
                text = font.render("%s"%loser.name, 1, (10, 10, 10))
                textpos = text.get_rect()
                textpos =(630,190) 
                screen.blit(text, textpos)
                #Display Loser score
                font = pygame.font.Font(None, 55)
                text = font.render("%s"%loser.score, 1, (10, 10, 10))
                textpos = text.get_rect()
                textpos =(880,347) 
                screen.blit(text, textpos)
            else:
                print "\n\n DEBUG:01\n\n"
                print "I think the winner is: %s (but this is where a draw should go!)"%winner
                #Loses the winner name (becomes None) until play again button is pressed
                screen.blit(dScreen,(0,0))
            #TODO: Put the next line in the reound debrieff scrin as wull
            drawBigAchievements(screen,newAchievementDisplay)                
            #Display play again and quit buttons
            screen.blit(playAgainButton, playAgainRect)
            screen.blit(quitButton,quitRect)
            mousePos=None
            event = pygame.event.poll()
            if event.type == MOUSEBUTTONDOWN:
                mousePos=event.pos
            if mousePos!=None:
                #process to restart a game using the `play again' button
                if  playAgainRect.collidepoint(mousePos):
                    conn.outgoing.put("END:PLAY AGAIN")
                    #state="AWAITING"
                    hazardDeck = None
                    actionDeck = None
                    stack = None
                    #state = ""
                    score = 0
                    inRDebrief=False
                    dbrfInfo=None
                    req=None
                    countdown=0.0
                    pName="no player"
                    oName="no player"
                    rndScores = ""
                    dbrfScores = []
                    winner = None
                    ingameDisplay = True
                    newAchievementDisplay=[]
                    break
                elif quitRect.collidepoint(mousePos):
                    quitGame()
                    break
            pygame.display.flip()

    # Display Review screen pair scores
    if len (dbrfScores) > 0 and inRDebrief == True:
        font = pygame.font.Font(None, 50)
        text = font.render("+ %s" %dbrfScores[0], 1,(255,255,255))
        textpos = text.get_rect()
        textpos = (312,298)
        screen.blit(text,textpos)
        #Pair 2 (h/a)
        font = pygame.font.Font(None, 50)
        text2 = font.render("+ %s" %dbrfScores[1], 1,(255,255,255))
        textpos2 = text2.get_rect()
        textpos2 = (632,298)
        screen.blit(text2,textpos2)

    #Start event loop
    for event in pygame.event.get():
        if event.type == QUIT:
            play=False
        #Use the mouse position to get the players card selection, add to stack.
        elif not inRDebrief and event.type == MOUSEBUTTONDOWN:
            if hoverCard != None:
                conn.outgoing.put("PLAY:%s"%hoverCard.code)
                myTurn = False
            else:
                myTurn = True

    #Use game clock to count down game review section (5 seconds)
    dt=clock.tick()
    if inRDebrief:
        countdown -= dt
        if countdown <= 0:
            inRDebrief = False
            #remove dbrfScores Item [0 and 1]
            dbrfScores = []
    pygame.display.flip()

#  LocalWords:  inRDebrief textpos makeLobby TODO dbrfScores rndScores pygame
#  LocalWords:  ingameDisplay playAgainButton mousePos elif wScreen blit drazah
#  LocalWords:  drawReviewScreen rScreen quitGame dealingLogo MOUSEBUTTONDOWN
#  LocalWords:  KEYDOWN inkey pos pAgainRect quitRect Rect drazaH sysfont exc
#  LocalWords:  recvfrom startswith gameAddress KeyboardInterrupt SystemExit xp
#  LocalWords:  len hazardDeck actionDeck newScore newState loadImage stackItem
#  LocalWords:  pName myTurn init fontobject req GAMEDETAILS pDetails dScreen
#  LocalWords:  smoothscale collidepoint rect xpos ncards playAgainRect oName
#  LocalWords:  quitButton hoverCard achievementGained AchGained RDEBRIEF OWINS
#  LocalWords:  addAchievement checkAchievement achievementList DISPLAYINFO yp
#  LocalWords:  achievementChecking displayWins displayLosses displayDraws eval
#  LocalWords:  displayOWins OPPONENTWINS OPPONENTLOSSES OPPONENTDRAWS ODRAWS
#  LocalWords:  OLOSSES displayOLosses displayODraws displayOAchs tDrawspos
#  LocalWords:  tLossespos tWinspos tDraws tLosses tWins startX startY sizeY
#  LocalWords:  tOWins tOLosses tODraws tOWinspos tOLossespos tODrawspos goLogo
#  LocalWords:  OACHS newAchievments animcount
